﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Willows.Model
{
	public partial class QueueReaderInterface
	{
		protected const string QueuePrefix = "queue_";

		#region Names
		// In queue names

		public string CanReadRegisterName
		{
			get { return QueuePrefix + LocalName + "_can_read_register"; }
		}

		public string DataInRegisterName
		{
			get { return LocalName; }
		}

		public string QueueReadSignalFlowGraphName
		{
			get { return QueuePrefix + LocalName + "_read"; }
		}

		public string QueueIdleSignalFlowGraphName
		{
			get { return QueuePrefix + LocalName + "_idle"; }
		}
		#endregion

		#region Gezel Expressions
		/// <summary>
		/// Get a constant expression with a given value and type
		/// </summary>
		/// <param name="type">Type of constant</param>
		/// <param name="value">Value of constant</param>
		/// <returns></returns>
		protected Weazel.Gezel.Model.Expression GetConstantExpression(Type type, string value)
		{
			ConstantExpression expression =
				new ConstantExpression(
					TypeRegistry,
					Type, value
				);

			return expression;
		}

		/// <summary>
		/// Get an identifier expression referencing the 'do_read' 
		/// signal of an in queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression doReadSignalExpression;
		protected Weazel.Gezel.Model.Expression DoReadSignalExpression
		{
			get
			{
				if (doReadSignalExpression == null)
				{
					doReadSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							attachedQueue.DoReadSignalName
						);
				}

				return doReadSignalExpression;
			}
		}

		/// <summary>
		/// Get an identifier expression referencing the 'can_read' 
		/// signal of an in queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression canReadSignalExpression;
		protected Weazel.Gezel.Model.Expression CanReadSignalExpression
		{
			get
			{
				if (canReadSignalExpression == null)
				{
					canReadSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							attachedQueue.CanReadSignalName
						);
				}

				return canReadSignalExpression;
			}
		}

		/// <summary>
		/// Get an identifier expression referencing the 'can_read' 
		/// register of an in queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression canReadRegisterExpression;
		protected Weazel.Gezel.Model.Expression CanReadRegisterExpression
		{
			get
			{
				if (canReadRegisterExpression == null)
				{
					canReadRegisterExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							CanReadRegisterName
						);
				}

				return canReadRegisterExpression;
			}
		}

		/// <summary>
		/// Get a reference to an expression referencing the 'data_in'
		/// signal (port) 
		/// </summary>
		private Weazel.Gezel.Model.Expression dataInSignalExpression;
		protected Weazel.Gezel.Model.Expression DataInSignalExpression
		{
			get
			{
				if (dataInSignalExpression == null)
				{
					dataInSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							Type,
							attachedQueue.DequeueSignalName
						);
				}

				return dataInSignalExpression;
			}
		}

		/// <summary>
		/// Get a reference to an expression referencing the 'data_in'
		/// register 
		/// </summary>
		private Weazel.Gezel.Model.Expression dataInRegisterExpression;
		protected Weazel.Gezel.Model.Expression DataInRegisterExpression
		{
			get
			{
				if (dataInRegisterExpression == null)
				{
					dataInRegisterExpression =
						new IdentifierExpression(
							TypeRegistry,
							Type,
							DataInRegisterName
						);
				}

				return dataInRegisterExpression;
			}
		}

		/// <summary>
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDataInRegisterExpression;
		public Weazel.Gezel.Model.Expression AssignDataInRegisterExpression
		{
			get
			{
				// create the expression if it does not already exist
				if (assignDataInRegisterExpression == null)
				{
					assignDataInRegisterExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DataInRegisterExpression,
							DataInSignalExpression
						);
				}

				return assignDataInRegisterExpression;
			}
		}

		/// <summary>
		/// Get a reference to an expression assigning the value of the incomming
		/// 'can_read' signal of the queue to its 'can_read_register'.
		/// </summary>
		private Weazel.Gezel.Model.Expression assignCanReadRegisterExpression;
		public Weazel.Gezel.Model.Expression AssignCanReadRegisterExpression
		{
			get
			{
				// create the expression if it does not already exist
				if (assignCanReadRegisterExpression == null)
				{
					assignCanReadRegisterExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							CanReadRegisterExpression,
							CanReadSignalExpression
						);
				}

				return assignCanReadRegisterExpression;
			}
		}

		/// <summary>
		/// Get a reference to an expression assigning the value '0' to the
		/// "do_read" signal of this queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDoReadZeroExpression;
		public Weazel.Gezel.Model.Expression AssignDoReadZeroExpression
		{
			get
			{
				// create the expression if it does not already exist
				if (assignDoReadZeroExpression == null)
				{
					assignDoReadZeroExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DoReadSignalExpression,
							GetConstantExpression((Type)TypeRegistry["boolean"], "0")
						);
				}

				return assignDoReadZeroExpression;
			}
		}

		/// <summary>
		/// Get a reference to an expression assigning the value '1' to the
		/// "do_read" signal of this queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDoReadOneExpression;
		public Weazel.Gezel.Model.Expression AssignDoReadOneExpression
		{
			get
			{
				// create the expression if it does not already exist
				if (assignDoReadOneExpression == null)
				{
					assignDoReadOneExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DoReadSignalExpression,
							GetConstantExpression((Type)TypeRegistry["boolean"], "1")
						);
				}

				return assignDoReadOneExpression;
			}
		}
		#endregion

		public virtual void Refine(RefinedModel.Model model)
		{
			if (!(Parent is Module))
				throw new NotSupportedException();

			// Parent must be of type module, cast
			Module module = Parent as Module;

			// check if another instance (clone) of this interface has already been refined
			if (module.RefinedReaderInterfaces.Contains(LocalName))
				return;

			// add this to the list of refined reader interfaces of the module
			module.RefinedReaderInterfaces.Add(LocalName);

			// refine the parent module (if it has not already been)
			module.Refine(model);

			// get a reference to it's datapath
			Weazel.Gezel.Model.Datapath datapath = module.Datapath;

			// Create the 'can_read' register for this queue
			Weazel.Gezel.Model.Register canReadRegister =
				new Weazel.Gezel.Model.Register(datapath, CanReadRegisterName, 1, false);

			if (datapath.Registers.ContainsKey(canReadRegister.LocalName))
			{
				string message = 
					string.Format("Internal error: register '{0}' already exists on datapath '{1}'", 
					canReadRegister.Name,
					datapath.Name
					);

				throw new ArgumentException(message);
			}

			// Add it to the datapath
			datapath.Registers.Add(canReadRegister.LocalName, canReadRegister);

			// The register should continiously be updated. 
			// Add an assignment expression to the 'always' sfg
			datapath.SignalFlowGraphs["always"].Expressions.Add(AssignCanReadRegisterExpression);


			Weazel.Gezel.Model.Register dataRegister =
				new Weazel.Gezel.Model.Register(datapath, DataInRegisterName, Type.BitWidth, false);

			datapath.Registers.Add(dataRegister.LocalName, dataRegister);


			// Create the sfg to be scheduled when this queue is being read
			Weazel.Gezel.Model.SignalFlowGraph readSfg =
				new Weazel.Gezel.Model.SignalFlowGraph(datapath, QueueReadSignalFlowGraphName);

			// Add an expression assigning one to the 'do_read' port to the sfg
			readSfg.Expressions.Add(AssignDoReadOneExpression);
			readSfg.Expressions.Add(AssignDataInRegisterExpression);

			// add
			datapath.SignalFlowGraphs.Add(readSfg.LocalName, readSfg);

			//
			// idle sfg
			//

			// Create the sfg to be scheduled when this queue is not being written
			Weazel.Gezel.Model.SignalFlowGraph idleSfg =
				new Weazel.Gezel.Model.SignalFlowGraph(datapath, QueueIdleSignalFlowGraphName);

			// Add an expression assigning 0 to the 'do_write' port ..
			idleSfg.Expressions.Add(AssignDoReadZeroExpression);

			// add sfg
			datapath.SignalFlowGraphs.Add(idleSfg.LocalName, idleSfg);
		}
	}
}
